package edu.mit.csail.relo.jdt.builder.asm;

import java.util.List;

import org.openrdf.model.Resource;
import org.openrdf.model.URI;

import edu.mit.csail.relo.ReloCore;
import edu.mit.csail.relo.jdt.CodeUnit;
import edu.mit.csail.relo.jdt.RJCore;
import edu.mit.csail.relo.jdt.builder.ReloRDFBuildProcessor;
import edu.mit.csail.relo.modelBridge.Artifact;
import edu.mit.csail.relo.modelBridge.DirectedRel;

// @tag design-todo: unify transformer design pattern
public class ResolveBrokenReferencesBuildProcessor extends ReloRDFBuildProcessor {

	static final URI resolvedBrokenReference = ReloCore.createReloURI("jdt-asm#resolvedBrokenReference");

	interface Transformer {
		void transform(Artifact codeArt);
	}
	
	class Joiner implements Transformer {
		public void transform(Artifact currMeth) {
	    	List<Artifact> references = currMeth.listArt(rdfRepo, DirectedRel.getFwd(RJCore.refType));
	    	for (Artifact ref : references) {
				if (rdfRepo.hasStatement(ref.elementRes, rdfRepo.rdfType, null)) continue;
				
				fixReferencesTo(currMeth, ref);
			}
		}

		
		private void fixReferencesTo(Artifact currMeth, Artifact ref) {
			//if (true) return;

			// @tag buggy: need to thoroughly test
			//System.err.println("Checking: " + ref);
			String refURI = ref.elementRes.toString();
			if (rdfRepo.hasStatement(ref.elementRes, resolvedBrokenReference, true)) {
				System.err.println("Already resolved: " + ref);
				return;
			}

			int typeNdx = refURI.lastIndexOf("$");
			int fldNdx = refURI.lastIndexOf(".");
			//System.err.println(typeNdx);
			//System.err.println(fldNdx);
			if (typeNdx == -1) return;
			if (fldNdx == -1) return;
			if (typeNdx > fldNdx) return;		// types - ignore
			String currFieldName = refURI.substring(fldNdx+1);
			// @tag test-this: remove below?
			if (currFieldName.contains(")")) return;
			//System.err.println(refURI.substring(0, fldNdx));
			Resource parentRefURI = rdfRepo.createURI(refURI.substring(0, fldNdx));
			Artifact parentRefArt = new Artifact(parentRefURI);
			//rdfRepo.dumpStatements(parentRefArt.elementRes, null, null);
			if (!rdfRepo.hasStatement(parentRefArt.elementRes, rdfRepo.rdfType, null)) return;	// parent also not set - ignore

			//System.err.println("Examining: " + ref + " fld: " + currFieldName);

    		getSuperClassesToCheckForOverrides(parentRefArt, ref, currFieldName);
			
		}

		private void getSuperClassesToCheckForOverrides(Artifact parentRefArt, Artifact currFieldLookingForOverrides, String currFieldName) {
			List<Artifact> superClasses = parentRefArt.listArt(rdfRepo, DirectedRel.getFwd(RJCore.inherits));
			for (Artifact superClass : superClasses) {
				checkForOverrides(superClass, currFieldLookingForOverrides, currFieldName);
	    		getSuperClassesToCheckForOverrides(superClass, currFieldLookingForOverrides, currFieldName);
			}
		}

		private void checkForOverrides(Artifact superClass, Artifact currFieldLookingForOverrides, String currFieldName) {
			List<Artifact> superFields = superClass.getChildrenArtifacts(rdfRepo, CodeUnit.getTypeFilter(rdfRepo, RJCore.fieldType));
			for (Artifact superField : superFields) {
    			String superFieldName = superField.getName(rdfRepo);
    			if (!currFieldName.equals(superFieldName)) continue;
    			
    			//System.err.println("Real Fixing: " + currFieldLookingForOverrides.elementRes+ " ===>>>> " + superField.elementRes);
    			// get all links to currFieldLookingForOverrides, and replace to superField
    			List<Artifact> refSources = currFieldLookingForOverrides.listArt(rdfRepo, DirectedRel.getRev(RJCore.refType));
    			for (Artifact refSrc : refSources) {
					rdfRepo.removeStatements(refSrc.elementRes, RJCore.refType, currFieldLookingForOverrides.elementRes);
					rdfRepo.addStatement(refSrc.elementRes, RJCore.refType, superField.elementRes);
				}
    			rdfRepo.addStatement(currFieldLookingForOverrides.elementRes, resolvedBrokenReference, true);
			}
		}
	}

	// @tag implement-this: otherwise db might get corrupt
	class Unjoiner implements Transformer {
		public void transform(Artifact codeArt) {
			if (true) return;
	    	List<Artifact> called = codeArt.listArt(rdfRepo, DirectedRel.getFwd(RJCore.calls));
	    	List<Artifact> calling = codeArt.listArt(rdfRepo, DirectedRel.getRev(RJCore.calls));
	    	for (Artifact callingMeth : calling) {
	        	for (Artifact calledMeth : called) {
	    			rdfRepo.removeStatements(callingMeth.elementRes, RJCore.calls, calledMeth.elementRes);
	    		}
			}
	    	rdfRepo.addStatement(codeArt.elementRes, rdfRepo.rdfType, RJCore.methodType);
		}
		
	}

	@Override
	public void finishProcessing() {
	    transform(projArt, new Joiner());
    }
    
	@Override
	public void cleanAnnotations() {
	    transform(projArt, new Unjoiner());
    }

    private void transform(Artifact codeArt, Transformer transformer) {
		// do a depth first traversal of the containment heirarchy while 'transforming'
		// 'transforming' = {preCondition: search for methods} and then search
		//   for broken references to join with reference from base

    	List<Artifact> children = codeArt.getChildrenArtifacts(rdfRepo);
    	for (Artifact child : children) {
			transform(child, transformer);
		}
    	if (!rdfRepo.hasStatement(codeArt.elementRes, RJCore.refType, null)) return;
    	
    	transformer.transform(codeArt);
	}

}
